#include "pch.h"
#include "cDirHelper.h"
#include "cDirTempFunc.h"

#include  <stdio.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <exception>
#include <stdexcept>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>

#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#include <io.h>
#include <stdlib.h>

#pragma  comment(lib, "Version.lib")
#else
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#endif // WIN32

namespace libtool_sdk
{
	namespace Helper
	{
 #ifdef _WIN32
		char g_strDllPath[256];
		char g_strDllVersion[256];
#endif

		TOOL_SDK_API bool DLL_SPEC	Tool_get_current_dir(char* buf, unsigned int* bufLen)
		{
			bool bRet = false;
#if defined (_WIN32)
			std::string result(0x8000, '\0');
            DWORD len = GetCurrentDirectoryA(static_cast<DWORD>(result.size()), &result[0]);
			if (len == 0 || len >= result.size())
			{
                //throw std::runtime_error("GetCurrentDirectory");
				printf("len == 0 || len >= result.size(), GetCurrentDirectoryA failed.\n");
                return false;
			}

			result.resize(len);
			size_t iBufLen = *bufLen;
			*bufLen = _snprintf(buf, iBufLen, "%s", result.c_str());
			bRet = true;
#else
			std::string strBuf;
			std::string::size_type buf_size = 1024;
			char * ret;
			do
			{
				strBuf.resize(buf_size);
				ret = getcwd(&strBuf[0], strBuf.size());
				if (!ret)
				{
                    //throw std::runtime_error("Tool_get_current_dir");
					printf("getcwd failed.\n");
					return false;
				}
			} while (!ret);

			strBuf.resize(std::strlen(strBuf.c_str()));

			size_t iBufLen = *bufLen;
			*bufLen = snprintf(buf, iBufLen, "%s", strBuf.c_str());
			bRet = true;

#endif
            return bRet;
		}

		TOOL_SDK_API bool DLL_SPEC Tool_split_path(std::vector<std::string> & components, std::size_t & special, std::string const & path)
		{
			if (path.empty())
			{
				return false;
			}
			components.reserve(10);
			special = 0;

			// First split the path into individual components separated by
			// system specific separator.

			path_sep_comp is_sep;
			Tool_split_into_components(components, path, is_sep);

			Tool_remove_empty(components, 0);
			if (components[0] == ".")
			{
				components.erase(components.begin());
				Tool_expand_relative_path(components, is_sep);
			}
			special = components.size();

			return true;
		}

		TOOL_SDK_API bool DLL_SPEC Tool_checkFileOrDirExsit(const char* dirName)
		{

#ifdef _WIN32
			if ((_access(dirName, 0)) == 0)
			{
				return true;
			}
#else
			if ((access(dirName, F_OK)) == 0)
			{
				return true;
			}
#endif // WIN32

			return false;
		}

		TOOL_SDK_API long DLL_SPEC Tool_make_directory(const char* dir)
		{
            //printf("%s dir = %s \n", __FUNCTION__, dir.c_str());
			if (dir == NULL)
			{
                printf("Tool_CheckFileOrDirExsit, dir is NULL.\n");
				return -1;
			}

#if defined (_WIN32)
#  if defined (UNICODE)
            if (_mkdir(dir) == 0)
#  else
			if (_mkdir(dir) == 0)
#  endif

#else
            //if (mkdir(std::string(dir).c_str(), 0777) == 0)
            int status = mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
            printf("%s dir = %s , = %d\n", __FUNCTION__, dir, status);
            if(status == 0)
#endif
				return 0;
			else
				return errno;
		}

		TOOL_SDK_API void DLL_SPEC Tool_make_dirs(const char* file_path)
		{
            //printf("%s file_path = %s\n", __FUNCTION__, file_path.c_str());
			if (file_path == NULL)
			{
                printf("Tool_CheckFileOrDirExsit, file_path is NULL.\n");
				return;
			}                

			std::vector<std::string> components;
			std::size_t special = 0;

			// Split file path into components.
			std::string strfile_path(file_path);

			if (!Tool_split_path(components, special, strfile_path))
				return;

			// Remove file name from path components list.
			if (components.size() <= 0)
			{
				return;
			}
            if (strfile_path.back() != '\\' && strfile_path.back() != '/')
			{
				components.pop_back();
			}

			// Loop over path components, starting first non-special path component.

			std::string path;
			//Helper::Tool_join(path, components.begin(), components.begin() + special, Helper::dir_sep);

			for (std::size_t i = 0, components_size = components.size(); i != components_size; ++i)
			{
				if (path.empty())
				{
#ifndef WIN32
                    path += "/";
#else
                    //path += dir_sep;
#endif
					path += components[i];
					//path += dir_sep;
				}
				else
				{
					path += dir_sep;
					path += components[i];
				}

				// Check whether path exists.
                if (Helper::Tool_checkFileOrDirExsit((path+dir_sep) .c_str()))
					// This directory exists. Move forward onto another path component.
					continue;

				// Make new directory.
                /*long const eno =*/ Helper::Tool_make_directory((path+dir_sep) .c_str());
			}
		}

		TOOL_SDK_API long DLL_SPEC Tool_getFile_size(const char* filename)
		{
			FILE *fp = fopen(filename, "r");
			if (!fp) { return -1; }
			fseek(fp, 0L, SEEK_END);
			long isize = ftell(fp);
			fclose(fp);

			return isize;
		}

		TOOL_SDK_API bool DLL_SPEC Tool_loadFile(const char* fileName, void* pBuffer, unsigned int* inputOutputFileSize)
		{
			if (NULL == fileName || NULL == pBuffer)
			{
				printf("Tool_LoadFile, NULL == fileName || NULL == pBuffer.\n");
				return false;
			}


			if (!Tool_checkFileOrDirExsit(fileName))
			{
				printf("Tool_LoadFile, File not Exist.\n");
				return false;
			}

			size_t iFileSize = Tool_getFile_size(fileName);

			if (iFileSize <= 0 || iFileSize > *inputOutputFileSize)
			{
                printf("Tool_LoadFile, iFileSize= %lu,  iFileSize <= 0 || iFileSize > inputFileSize \n", iFileSize);
				return false;
			}
			*inputOutputFileSize = iFileSize;

			FILE* pFile = NULL;
			//errno_t errCode;
			//_set_errno(0);
			//errCode = fopen_s(&pFile, fileName, "rb");
			pFile = fopen(fileName, "rb");
			if (NULL != pFile)
			{
				fread(pBuffer, iFileSize, 1, pFile);
				fclose(pFile);
				pFile = NULL;

				return true;
			}
			else
			{
				//printf("Tool_LoadFile, open file failed, error code = %d.\n", errCode);
				printf("Tool_LoadFile, open file failed.\n");
				return false;
			}
		}
		
		TOOL_SDK_API bool DLL_SPEC Tool_save_file(const char* fileName, void* pData, unsigned int  dataSize)
		{
			if (NULL == fileName
				|| NULL == pData
				|| dataSize <= 0)
			{
				return false;
			}

			if (!Tool_checkFileOrDirExsit(fileName))
			{
				Tool_make_dirs(fileName);
			}

			std::ofstream ofstreamlogFile;
			ofstreamlogFile.exceptions(std::ofstream::badbit | std::ofstream::failbit);			
			try
			{
				ofstreamlogFile.open(fileName, std::ios::binary | std::ios::trunc | std::ios::out);
				if (ofstreamlogFile.is_open())
				{
					ofstreamlogFile.write(reinterpret_cast<char*>(pData), dataSize);
					return true;
				}
				else
				{
					return false;
				}
			}
			catch (std::ofstream::failure &e)
			{
				std::cerr << e.what() << std::endl;
				return false;
			}
		}

		TOOL_SDK_API bool  DLL_SPEC Tool_get_dll_path(char* buf, unsigned int* bufLen)
		{
			bool bRet = false;
#ifdef _WIN32
            if(strlen(g_strDllPath) == 0)
            {
				bRet = Tool_get_current_dir(buf, bufLen);
                printf("current platform is win32, but DllPath is empty, use work dir %s instead.\n", buf);
                return bRet;
            }
			int iSize = sprintf(buf, "%s", g_strDllPath);
			*bufLen = iSize;
			return iSize >= 0 ? true : false;
#else
            static std::string wdir;
            if(wdir.empty())
            {
				Dl_info dl_info;
            	dladdr((void*)libtool_sdk::Helper::Tool_get_dll_path, &dl_info);

            	std::string path(dl_info.dli_fname);
            	wdir = path.substr(0, path.find_last_of('/') + 1);

                size_t iPos = wdir.find_first_of(".");
                printf("find_first_of . = %lu \n", iPos);
                if(iPos == 0)
                {
                    printf("replace string.\n");
					char chValue[512];
					getcwd(chValue, sizeof(chValue));
                    wdir.replace(iPos, strlen("."), chValue);
                }
                printf("final string = %s\n", wdir.c_str());
            }
			int iSize = sprintf(buf, "%s", wdir.c_str());
			*bufLen = iSize;
			return iSize >= 0 ? true : false;
#endif
		}

#ifdef _WIN32
		TOOL_SDK_API void DLL_SPEC Tool_set_dll_path(const char* path)
        {
			if(NULL == path)
                return;
			size_t iDllsize = sizeof(g_strDllPath);
			size_t iPathsize = strlen(path);
            printf("iDllsize = %lu, iPathsize = %lu\n", iDllsize, iPathsize);
			if (sizeof(g_strDllPath) > iPathsize)
			{
				memset(g_strDllPath, '\0', sizeof(g_strDllPath));
				sprintf(g_strDllPath, "%s", path);
			}
        }

		TOOL_SDK_API bool  DLL_SPEC Tool_getFileVersionInfo(const char* szVersionFile, char* szVersionInfo, unsigned int  iVersionInfoLen)
		{
			DWORD  verHandle = 0;
			UINT   size = 0;
			LPBYTE lpBuffer = NULL;
            DWORD  verSize = GetFileVersionInfoSizeA(szVersionFile, &verHandle);

			std::string strInfo;

            if (verSize != 0)
			{
				LPSTR verData = new char[verSize];

                if (GetFileVersionInfoA(szVersionFile, verHandle, verSize, verData))
				{
                    if (VerQueryValueA(verData, "\\", (VOID FAR* FAR*)&lpBuffer, &size))
					{
						if (size)
						{
							VS_FIXEDFILEINFO *verInfo = (VS_FIXEDFILEINFO *)lpBuffer;
							if (verInfo->dwSignature == 0xfeef04bd)
							{
								char chVersion[256] = {0};
								// Doesn't matter if you are on 32 bit or 64 bit,
								// DWORD is always 32 bits, so first two revision numbers
								// come from dwFileVersionMS, last two come from dwFileVersionLS
                                sprintf(chVersion, "%lu.%lu.%lu.%lu",
									(verInfo->dwFileVersionMS >> 16) & 0xffff,
									(verInfo->dwFileVersionMS >> 0) & 0xffff,
									(verInfo->dwFileVersionLS >> 16) & 0xffff,
									(verInfo->dwFileVersionLS >> 0) & 0xffff
									);
								strInfo = chVersion;
							}
						}
					}
				}
				delete[] verData;
			}
			if (!strInfo.empty() && strInfo.size() < iVersionInfoLen)
			{
                sprintf(szVersionInfo, "%s", strInfo.c_str());
			}
		
			return strInfo.empty() ? true : false;
		}

		TOOL_SDK_API void DLL_SPEC Tool_set_dll_versionInfo(const char* info)
		{
			if (NULL != info && strlen(info) < sizeof(g_strDllVersion))
			{
				memset(g_strDllVersion, 0, sizeof(g_strDllVersion));
				strcpy(g_strDllVersion, info);
			}
		}

		TOOL_SDK_API bool  DLL_SPEC Tool_get_dll_versionInfo(char* info, unsigned int* bufLen)
		{
			if (NULL == info || NULL == bufLen || *bufLen < sizeof(g_strDllVersion))
			{
				printf("Tool_get_dll_versionInfo, bufLen is too small.\n");
				return false;
			}
			if (strlen(g_strDllVersion) == 0)
			{
				printf("Tool_get_dll_versionInfo, g_strDllVersion is empty.\n");
				return false;
			}
			*bufLen = sprintf(info, "%s", g_strDllVersion);
            return true;
        }

#else
        TOOL_SDK_API int DLL_SPEC Tool_copyfile_linux(const char* to, const char* from)
        {
            int fd_to, fd_from;
             char buf[4096];
             ssize_t nread;
             int saved_errno;

             fd_from = open(from, O_RDONLY);
             if (fd_from < 0)
                 return -1;

             fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666);
             if (fd_to < 0)
                 goto out_error;

             while (nread = read(fd_from, buf, sizeof buf), nread > 0)
             {
                 char *out_ptr = buf;
                 ssize_t nwritten;

                 do {
                     nwritten = write(fd_to, out_ptr, nread);

                     if (nwritten >= 0)
                     {
                         nread -= nwritten;
                         out_ptr += nwritten;
                     }
                     else if (errno != EINTR)
                     {
                         goto out_error;
                     }
                 } while (nread > 0);
             }

             if (nread == 0)
             {
                 if (close(fd_to) < 0)
                 {
                     fd_to = -1;
                     goto out_error;
                 }
                 close(fd_from);

                 /* Success! */
                 return 0;
             }

           out_error:
             saved_errno = errno;

             close(fd_from);
             if (fd_to >= 0)
                 close(fd_to);

             errno = saved_errno;
             return -1;
        }
#endif

        TOOL_SDK_API int DLL_SPEC Tool_copyfile_crossPlatform(const char *destination, const char *source)
        {
            int iRet = -1;
#ifdef _WIN32
            if(CopyFileA(source, destination, FALSE))
            {
                iRet = 0;
            }
#else
            iRet = Tool_copyfile_linux(destination, source);
#endif
            return iRet;
        }

		TOOL_SDK_API bool DLL_SPEC Tool_get_real_path(const char* partialPath, char* absolutePathsBuf, unsigned int* bufLen)
		{
			bool bGetSuccess = false;
			
#ifdef _WIN32
			char full[_MAX_PATH];
			if( _fullpath( full, partialPath, _MAX_PATH ) != NULL )
			{
				*bufLen = sprintf(absolutePathsBuf, "%s", full);
				bGetSuccess = true;
			}
#else
			char full[PATH_MAX] = {0};
    		if(realpath(partialPath,full) != NULL)
			{
				*bufLen = sprintf(absolutePathsBuf, "%s", full);
				bGetSuccess = true;
			}
#endif
			return bGetSuccess;
		}

	}
}
